package cau.se.project.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import cau.se.project.Partitioning;
import cau.se.project.data.method.GroupControl;
import cau.se.project.data.method.RowControl;
import cau.se.project.method.CLSXControl;
import cau.se.project.method.DSMControl;

public class CLSXTreeModel implements TreeModel, CLSXControl {

	public static final String ROOTNAME = "$ROOT";

	private GroupControl root = new CLSXGroup(ROOTNAME);
	private DSMControl dsm;

	private TreeModelListener listener = null;

	// 새 tree를 만든다
	public CLSXTreeModel()
	{
		this.dsm = new DependencyStructureMatrix();
	}

	// 인자로 들어온 dsm을 토대로 기본 트리를 구성한다
	public CLSXTreeModel(DSMControl dsm)
	{
		this.dsm = dsm;
		resetTree();
	}

	@Override
	public DSMControl getDSM()
	{
		return dsm;
	}

	@Override
	public void addRow(RowControl row)
	{
		try {
			dsm.insertRow(row, null);
			root.addGroupMember(row);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if(listener != null) {
			TreeModelEvent e = null;
			listener.treeNodesInserted(e);
		}
	}

	@Override
	public Object getRoot() {
		return root;
	}

	@Override
	public Object getChild(Object parent, int index) {
		RowControl p = (RowControl)parent;
		return p.getChildAt(index);
	}

	@Override
	public int getChildCount(Object parent) {
		RowControl p = (RowControl)parent;
		return p.getChildCount();
	}

	@Override
	public boolean isLeaf(Object node) {
		RowControl n = (RowControl)node;
		for(int i=0; i<dsm.getSize(); i++) {
			if(n == dsm.getRow(i))
				return n.isLeaf();
		}
		
		return false;
	}

	@Override
	public void valueForPathChanged(TreePath path, Object newValue) {
		// TODO Auto-generated method stub

	}

	@Override
	public int getIndexOfChild(Object parent, Object child) {
		GroupControl p = (GroupControl)parent;
		return p.getIndex((TreeNode)child);
	}

	@Override
	public void addTreeModelListener(TreeModelListener l) {
		this.listener = l;
	}

	@Override
	public void removeTreeModelListener(TreeModelListener l) {
		listener = null;
	}

	@Override
	public GroupControl makeGroup(String newName) {
		return new CLSXGroup(newName);
	}

	@Override
	public void unGroup(GroupControl group)
	{
		// root는 upgroup할 수 없음
		if(group == root)
			return;
		
		try {
			System.out.println("ungroup " + group.getName());
			RowControl rc;
			GroupControl parent = (GroupControl) group.getParent();

			// treat elements
			for(int i=0; i<group.getChildCount(); i++) {
				//pop
				rc = (RowControl) group.getChildAt(i);

				//push
				parent.insertGroupMember(rc, group);
			}
			parent.removeGroupMember(group);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public RowControl makeRow(String name) {
		return dsm.makeRow(name);
	}

	@Override
	public void removeRow(RowControl row) {
		try {
			root.removeGroupMember(row);
			dsm.removeRow(row);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void moveRowTo(int rowIndex, int rowPosition) throws Exception {
		if(rowIndex == rowPosition)	// 자기자신 이동은 허용하지 않음
			return;
		
		RowControl row = dsm.getRow(rowIndex);
		RowControl position = dsm.getRow(rowPosition);
		
		if(position != null && row.getParent() != position.getParent()) {
			throw new Exception("different parent!\n" + row.getName() + "\n" + position.getName());
		}
		// tree
		// pop
		GroupControl parent = (GroupControl) row.getParent();
		parent.removeGroupMember(row);
		// push
		parent.insertGroupMember(row, position);
		
		// dsm
		dsm.removeRow(row);
		dsm.insertRow(row, position);
	}
	
	@Override
	public void moveRowTo(RowControl rcMove, RowControl rcPosition, boolean bFrontof) throws Exception
	{
		System.out.println("in moveRowTo..");
		if(rcMove == rcPosition) {
			System.out.println("same node");
			return;
		}
		
		if(rcMove.isLeaf() == false && ((GroupControl)rcMove).findRowByName(rcPosition.getName()) != null) {
			//throw new Exception("cannot move to child element");
			System.out.println("cannot move to child element");
			return;
		}
		
		if(rcPosition == root || rcMove == root) {
			throw new Exception("cannot modify root node!");
		}
		
		// tree
		// pop
		((GroupControl)rcMove.getParent()).removeGroupMember(rcMove);
		// push
		// 들어갈 위치 선정
		RowControl fixedRcPosition = rcPosition;
		if(bFrontof == false) {
			GroupControl parent = (GroupControl) rcPosition.getParent();
			for(int i=0; i<parent.getChildCount(); i++) {
				if(parent.getChildAt(i) == rcPosition) {
					if(i == parent.getChildCount()-1)
						fixedRcPosition = null;
					else
						fixedRcPosition = (RowControl) parent.getChildAt(i+1);
					break;
				}
			}
		}
		// 넣음
		((GroupControl)rcPosition.getParent()).insertGroupMember(rcMove, fixedRcPosition);
		
		
		// dsm
		// 모든 행을 일시 제거하고 dsm을 재생성한다
		while(dsm.getSize() != 0) {
			dsm.removeRow(dsm.getRow(0));
		}
		makeDSMFromTree(root);
	}

	private void makeDSMFromTree(GroupControl gc)
	{
		for(int i=0; i<gc.getChildCount(); i++) {
			RowControl rc = (RowControl) gc.getChildAt(i);
			if(rc.isLeaf()) {
				//System.out.println("push row " + rc.getName());
				dsm.insertRow(rc, null);
			} else
				makeDSMFromTree((GroupControl) rc);
		}
	}

	@Override
	public void sortMatrix(GroupControl gc) throws Exception {
		// 정렬을 위한 리스트 구성
		List<RowControl> children = new ArrayList<>();
		while( gc.getChildCount() > 0 ) {
			children.add((RowControl) gc.getChildAt(0));
			gc.removeGroupMember(gc.getChildAt(0));
		}
		
		// 정렬
		Collections.sort(children, new Comparator<RowControl>() {

			@Override
			public int compare(RowControl o1, RowControl o2) {
				return o1.getName().compareTo(o2.getName());
			}
			
		});
		
		for(int i=0; i<children.size(); i++) {
			gc.addGroupMember(children.get(i));
		}
		
		// dsm 재구성
		while( dsm.getSize() > 0 )
			dsm.removeRow(dsm.getRow(0));
		makeDSMFromTree(root);
	}

	@Override
	public void doPartition() {
		CLSXTreeModel newTree = new CLSXTreeModel();
		List<RowControl> rowStack = new LinkedList<RowControl>();
		HashMap<RowControl, List<RowControl>> tempDependencyMap = new HashMap<RowControl, List<RowControl>>();
		
		for ( int i = 0; i < this.dsm.getSize(); i++ ) {
			tempDependencyMap.put(this.dsm.getRow(i), new ArrayList<RowControl>(this.dsm.getRow(i).getDepenencyList()));
		}

		for ( int i = 0; i < this.dsm.getSize(); i++ ) {
			RowControl tempRow = this.dsm.getRow(i);
			
			if ( tempRow.getDepenencyList().isEmpty() ) {
				try {
					for ( int j = 0; j < this.dsm.getSize(); j++ ) {
						this.dsm.getRow(j).removeDependency(tempRow);
					}
					for ( RowControl dependencyRow : tempDependencyMap.get(tempRow) ) {
						tempRow.setDependency(dependencyRow);
					}
					newTree.addRow(tempRow);
					this.removeRow(tempRow);
					i = -1;
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}				
			}
		}


		for ( int i = 0; i < this.dsm.getSize(); i++ ) {
			if ( isEmptyCol(this.dsm, i) ) {
				rowStack.add(this.dsm.getRow(i));
				
				for ( int j = 0; j < this.dsm.getSize(); j++ ) {
					//this.removeRow(this.dsm.getRow(i));
					this.dsm.getRow(j).removeDependency(this.dsm.getRow(i));
				}
				this.removeRow(this.dsm.getRow(i));
				i = -1;
			}
		}


		// handling cyclic row.
		for ( int i = 0; i < this.dsm.getSize(); i++ ) {
			RowControl tempRow = this.dsm.getRow(i);
			//newTree.addRow(tempRow);
			
			for ( int j = 0; j < this.dsm.getSize(); j++ ) {
				this.dsm.getRow(j).removeDependency(tempRow);
			}
			for ( RowControl dependencyRow : tempDependencyMap.get(tempRow) ) {
				tempRow.setDependency(dependencyRow);
			}
			newTree.addRow(tempRow);
			this.removeRow(tempRow);
			i = -1;
		}
		
		
		for ( int i = rowStack.size() - 1; i >= 0; i-- ) {
			RowControl tempRow = rowStack.get(i);
			for ( RowControl dependencyRow : tempDependencyMap.get(tempRow) ) {
				tempRow.setDependency(dependencyRow);
			}
			
			try {
				newTree.addRow(tempRow);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		this.root = (GroupControl) newTree.getRoot();
		this.dsm = newTree.getDSM(); 
		/*int[][] array = {{0,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,0,0,0,0,0},
				{1,0,0,0,0,0,0,0,0,0},
				{1,0,0,0,0,0,0,0,0,0},
				{0,1,0,0,0,0,0,0,0,0},
				{0,0,0,0,0,1,0,0,0,0},
				{0,0,0,0,0,0,0,0,1,0},
				{0,0,0,0,0,0,0,1,0,1},
				{0,0,0,0,0,0,0,1,1,0}};
		DSMControl dsm = makeDSMFromArray(array);
		for(int i=0; i<array.length; i++)
			dsm.getRow(i).setName(String.valueOf(i));
		dsm.print();*/
	}

	private boolean isEmptyCol(DSMControl dsm, int i) {
		RowControl that = dsm.getRow(i);
		
		for ( int j = 0; j < dsm.getSize(); j++ ) {
			if ( dsm.getRow(j).getDepenencyList().contains(that) ) {
				return false;
			}
		}

		return true;
	}

	@Override
	public int[][] getArray() {
		return dsm.toArray();
	}

	@Override
	public void resetTree() {
		try{
			System.out.println("reset trees");

			// remove all childs
			while(root.getChildCount() != 0) {
				root.removeGroupMember(root.getChildAt(0));
			}

			// add dsm's row newly
			for(int i=0; i<dsm.getSize(); i++) {
				root.addGroupMember(dsm.getRow(i));
			}

			System.out.println("tree childs: " + getChildCount(root));
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public String[] getNameList() {
		if(dsm.getSize() == 0)
			return null;

		String[] list = new String[dsm.getSize()];
		for(int i=0; i<list.length; i++) {
			list[i] = dsm.getRow(i).getName();
		}

		return list;
	}

	@Override
	public void setDependency(int from, int to, boolean isDependency) {
		RowControl rowFrom = dsm.getRow(from);
		RowControl rowTo = dsm.getRow(to);

		if(rowFrom == null || rowTo == null)	// 에러
			return;

		System.out.println(String.format("set dependency %s to %s, %s", rowFrom.getName(), rowTo.getName(), isDependency));

		if(isDependency) {
			rowFrom.setDependency(rowTo);
		}
		else {
			rowFrom.removeDependency(rowTo);
		}

	}

	@Override
	public void moveUp(RowControl row) throws Exception {
		if(row == null || root == row) return;
		
		int index = -1;
		GroupControl parent = (GroupControl) row.getParent();
		for(int i=0; i<parent.getChildCount(); i++) {
			if(parent.getChildAt(i) == row) {
				index = i;
				break;
			}
		}
		
		if(index <= 0)
			return;
		
		moveRowTo(row, (RowControl) parent.getChildAt(index-1), true);
	}

	@Override
	public void moveDown(RowControl row) throws Exception {
		if(row == null || root == row) return;
		
		int index = -1;
		GroupControl parent = (GroupControl) row.getParent();
		for(int i=0; i<parent.getChildCount(); i++) {
			if(parent.getChildAt(i) == row) {
				index = i;
				break;
			}
		}
		
		if(index < 0 || index == parent.getChildCount()-1)
			return;
		
		moveRowTo(row, (RowControl) parent.getChildAt(index+1), false);
	}
	
	public DSMControl makeDSMFromArray(int[][] array)
	{
		DSMControl dsm = new DependencyStructureMatrix();
		RowControl[] rows = new RowControl[array.length];
		
		for(int i=0; i<array.length; i++) {
			rows[i] = makeRow(null);
			dsm.insertRow(rows[i], null);
		}
		
		for(int i=0; i<array.length; i++) {
			for(int j=0; j<array.length; j++) {
				if(array[i][j] != 0) {
					rows[i].setDependency(rows[j]);
				}
			}
		}
		
		return dsm;
	}
}
